package main

import (
	"fmt"
	"math/rand"
	"time"
)

func msgGen(name string, timeInterval int) <-chan string {
	m := make(chan string)
	go func() {
		i := 0
		for {
			time.Sleep(time.Duration(rand.Intn(timeInterval)) * time.Millisecond)
			m <- fmt.Sprintf("service；%s, message %d", name, i)
			i++
		}
	}()
	return m
}
func msgGenBreak(name string, timeInterval int, done chan struct{}) <-chan string {
	m := make(chan string)
	go func() {
		i := 0
		for {
			select {
			case <-time.After(time.Duration(rand.Intn(timeInterval)) * time.Millisecond):
				m <- fmt.Sprintf("service；%s, message %d", name, i)
			case <-done:
				fmt.Println("cleaning up....")
				time.Sleep(2 * time.Second)
				fmt.Println("cleanup done")
				done <- struct{}{}
				return
			}
			i++
		}
	}()
	return m
}

func fanIn(c1, c2 <-chan string) chan string {
	c := make(chan string)
	go func() {
		for {
			c <- <-c1
		}
	}()

	go func() {
		for {
			c <- <-c2
		}

	}()

	return c
}

func fanInToMore(chas ...<-chan string) chan string {
	c := make(chan string)
	for _, cha := range chas {

		//因为 for 循环里开了 goroutine，这里需要使用局部变量接一下，
		//否则 for 循环中的 cha 是全局变量，for 循环执行完，cha 变成了最后一个 chan，此时 goroutine 还没执行呢
		//就会出现 goroutine 中的 cha 永远是最后一个 chan；这是个 go 语言中的坑
		//可以使用下边的写法
		//cha := cha
		//go func() {
		//	for {
		//		c <- <-cha
		//	}
		//
		//}()

		//也可以使用这样的写法
		go func(in <-chan string) {
			for {
				c <- fmt.Sprint("for 循环使用变量", <-cha)
			}
		}(cha)
	}

	return c
}

func fanInBySelect(c1, c2 <-chan string) chan string {
	c := make(chan string)
	go func() {
		//注意select 外边这个 for 循环，一直在 select
		for {
			select {
			case m := <-c1:
				c <- m
			case m := <-c2:
				c <- m
			}
		}
	}()

	return c
}

func noBlockingWait(c <-chan string) (string, bool) {
	select {
	case m := <-c:
		return m, true
	default:
		return "", false
	}
}

func timeoutWait(c <-chan string, timeout time.Duration) (string, bool) {
	select {
	case m := <-c:
		return m, true
	case <-time.After(timeout):
		return "", false
	}
}

func main() {
	//m1 := msgGen("message1", 200)
	//m2 := msgGen("message2", 2000)

	//m := fanIn(m1, m2)
	//m := fanInBySelect(m1, m2)
	//m := fanInToMore(m1, m2)
	//for {
	//	fmt.Println(<-m)
	//}

	//for true {
	//	fmt.Println(<-m1)
	//	if msg, ok := noBlockingWait(m2); ok {
	//		fmt.Println(msg)
	//	} else {
	//		fmt.Println("there is no message for m2...")
	//	}
	//}

	//for true {
	//	fmt.Println(<-m1)
	//	if msg, ok := timeoutWait(m2, 1000); ok {
	//		fmt.Println(msg)
	//	} else {
	//		fmt.Println("there is no message for m2...")
	//	}
	//}

	done := make(chan struct{})
	m1 := msgGenBreak("message1", 200, done)
	for i := 0; i < 5; i++ {
		fmt.Println(<-m1)
	}

	done <- struct{}{}
	<-done

}
